﻿using System;
using System.Linq.Expressions;
using Library.Distributed;

namespace Library.Linq.Distributed
{
    public static class Distributed
    {
        public static IDistributed<T> Compose<T>(this T item)
        {
            return Context.Current.Compose(item);
        }
        public static IDistributed<T> Compose<T>(this T item, bool include)
        {
            return Context.Current.Compose(item, include);
        }

        public static Library.Collections.Generic.IVectorMultidimensionalReadOnly<T> AllGather<T>(this T item)
        {
            return Context.Current.AllGather(item);
        }
        public static Library.Collections.Generic.IVectorMultidimensionalReadOnly<T> AllToAll<T>(this Library.Collections.Generic.IVectorMultidimensionalReadOnly<T> source)
        {
            return Context.Current.AllToAll(source);
        }

        public static T Aggregate<T>(this IDistributed<T> distributed, Expression<Func<T, T, T>> reduction)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Reduce(x, reduction));
        }
        public static bool All(this IDistributed<bool> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.All(x));
        }
        public static bool All<T>(this IDistributed<T> distributed, Expression<Func<T, bool>> predicate)
        {
            return Platform.Current.Invoke(distributed, predicate, (ctx, x) => ctx.All(x));
        }

        public static uint And(this IDistributed<uint> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.And(x));
        }
        public static ulong And(this IDistributed<ulong> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.And(x));
        }
        public static uint And<T>(this IDistributed<T> distributed, Expression<Func<T, uint>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.And(x));
        }
        public static ulong And<T>(this IDistributed<T> distributed, Expression<Func<T, ulong>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.And(x));
        }
        
        public static bool Any(this IDistributed<bool> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Any(x));
        }
        public static bool Any<T>(this IDistributed<T> distributed)
        {
            return distributed.Context.Count > 0;
        }
        public static bool Any<T>(this IDistributed<T> distributed, Expression<Func<T, bool>> predicate)
        {
            return Platform.Current.Invoke(distributed, predicate, (ctx, x) => ctx.Any(x));
        }

        public static ulong Count<T>(this IDistributed<T> distributed)
        {
            return distributed.Context.Count;
        }
        public static ulong Count<T>(this IDistributed<T> distributed, Expression<Func<T, bool>> predicate)
        {
            return Platform.Current.Invoke(distributed, predicate, (ctx, x) => ctx.Sum(x));
        }

        public static uint ExclusiveOr(this IDistributed<uint> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.ExclusiveOr(x));
        }
        public static ulong ExclusiveOr(this IDistributed<ulong> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.ExclusiveOr(x));
        }
        public static uint ExclusiveOr<T>(this IDistributed<T> distributed, Expression<Func<T, uint>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.ExclusiveOr(x));
        }
        public static ulong ExclusiveOr<T>(this IDistributed<T> distributed, Expression<Func<T, ulong>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.ExclusiveOr(x));
        }

        public static decimal Max(this IDistributed<decimal> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static float Max(this IDistributed<float> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static double Max(this IDistributed<double> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static sbyte Max(this IDistributed<sbyte> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static short Max(this IDistributed<short> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static int Max(this IDistributed<int> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static long Max(this IDistributed<long> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static byte Max(this IDistributed<byte> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static ushort Max(this IDistributed<ushort> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static uint Max(this IDistributed<uint> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static ulong Max(this IDistributed<ulong> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static T Max<T>(this IDistributed<T> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Max(x));
        }
        public static decimal Max<T>(this IDistributed<T> distributed, Expression<Func<T, decimal>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static float Max<T>(this IDistributed<T> distributed, Expression<Func<T, float>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static double Max<T>(this IDistributed<T> distributed, Expression<Func<T, double>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static sbyte Max<T>(this IDistributed<T> distributed, Expression<Func<T, sbyte>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static short Max<T>(this IDistributed<T> distributed, Expression<Func<T, short>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static int Max<T>(this IDistributed<T> distributed, Expression<Func<T, int>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static long Max<T>(this IDistributed<T> distributed, Expression<Func<T, long>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static byte Max<T>(this IDistributed<T> distributed, Expression<Func<T, byte>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static ushort Max<T>(this IDistributed<T> distributed, Expression<Func<T, ushort>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static uint Max<T>(this IDistributed<T> distributed, Expression<Func<T, uint>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static ulong Max<T>(this IDistributed<T> distributed, Expression<Func<T, ulong>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }
        public static TResult Max<T, TResult>(this IDistributed<T> distributed, Expression<Func<T, TResult>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Max(x));
        }

        public static decimal Min(this IDistributed<decimal> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static float Min(this IDistributed<float> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static double Min(this IDistributed<double> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static sbyte Min(this IDistributed<sbyte> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static short Min(this IDistributed<short> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static int Min(this IDistributed<int> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static long Min(this IDistributed<long> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static byte Min(this IDistributed<byte> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static ushort Min(this IDistributed<ushort> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static uint Min(this IDistributed<uint> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static ulong Min(this IDistributed<ulong> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static T Min<T>(this IDistributed<T> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Min(x));
        }
        public static decimal Min<T>(this IDistributed<T> distributed, Expression<Func<T, decimal>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static float Min<T>(this IDistributed<T> distributed, Expression<Func<T, float>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static double Min<T>(this IDistributed<T> distributed, Expression<Func<T, double>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static sbyte Min<T>(this IDistributed<T> distributed, Expression<Func<T, sbyte>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static short Min<T>(this IDistributed<T> distributed, Expression<Func<T, short>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static int Min<T>(this IDistributed<T> distributed, Expression<Func<T, int>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static long Min<T>(this IDistributed<T> distributed, Expression<Func<T, long>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static byte Min<T>(this IDistributed<T> distributed, Expression<Func<T, byte>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static ushort Min<T>(this IDistributed<T> distributed, Expression<Func<T, ushort>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static uint Min<T>(this IDistributed<T> distributed, Expression<Func<T, uint>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static ulong Min<T>(this IDistributed<T> distributed, Expression<Func<T, ulong>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }
        public static TResult Min<T, TResult>(this IDistributed<T> distributed, Expression<Func<T, TResult>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Min(x));
        }

        public static uint Or(this IDistributed<uint> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Or(x));
        }
        public static ulong Or(this IDistributed<ulong> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Or(x));
        }
        public static uint Or<T>(this IDistributed<T> distributed, Expression<Func<T, uint>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.Or(x));
        }
        public static ulong Or<T>(this IDistributed<T> distributed, Expression<Func<T, ulong>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.Or(x));
        }

        public static ulong Sum(this IDistributed<bool> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static decimal Sum(this IDistributed<decimal> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static float Sum(this IDistributed<float> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static double Sum(this IDistributed<double> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static sbyte Sum(this IDistributed<sbyte> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static short Sum(this IDistributed<short> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static int Sum(this IDistributed<int> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static long Sum(this IDistributed<long> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static byte Sum(this IDistributed<byte> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static ushort Sum(this IDistributed<ushort> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static uint Sum(this IDistributed<uint> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static ulong Sum(this IDistributed<ulong> distributed)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Sum(x));
        }
        public static ulong Sum<T>(this IDistributed<T> distributed, Expression<Func<T, bool>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static decimal Sum<T>(this IDistributed<T> distributed, Expression<Func<T, decimal>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static float Sum<T>(this IDistributed<T> distributed, Expression<Func<T, float>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static double Sum<T>(this IDistributed<T> distributed, Expression<Func<T, double>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static sbyte Sum<T>(this IDistributed<T> distributed, Expression<Func<T, sbyte>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static short Sum<T>(this IDistributed<T> distributed, Expression<Func<T, short>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static int Sum<T>(this IDistributed<T> distributed, Expression<Func<T, int>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static long Sum<T>(this IDistributed<T> distributed, Expression<Func<T, long>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static byte Sum<T>(this IDistributed<T> distributed, Expression<Func<T, byte>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static ushort Sum<T>(this IDistributed<T> distributed, Expression<Func<T, ushort>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static uint Sum<T>(this IDistributed<T> distributed, Expression<Func<T, uint>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }
        public static ulong Sum<T>(this IDistributed<T> distributed, Expression<Func<T, ulong>> function)
        {
            return Platform.Current.Invoke(distributed, function, (ctx, x) => ctx.Sum(x));
        }

        public static IDistributed<T> Where<T>(this IDistributed<T> distributed, Expression<Func<T, bool>> predicate)
        {
            return Platform.Current.Invoke(distributed, x => x, (ctx, x) => ctx.Compose(x, predicate));
        }
        public static IDistributed<TResult> Select<T, TResult>(this IDistributed<T> distributed, Expression<Func<T, TResult>> functor)
        {
            return Platform.Current.Invoke(distributed, functor, (ctx, x) => ctx.Compose(x));
        }
    }
}
